<template>
  <view class="scs-voice-input" @click="onClickInside">
    <view class="scs-voice-input-content">
      <uni-easyinput
        ref="inputObj"
        v-model="innerValue"
        v-bind="withDefaultAttrs"
        :type="type"
        :inputBorder="false"
        v-on="$listeners"
        @focus="onFocus"
      ></uni-easyinput>
      <slot name="footer"></slot>
    </view>
    <!-- #ifdef APP-PLUS -->
    <view class="scs-voice-input-voice" ref="toolBar" v-if="insideFocus">
      <view class="send-edit">
        <view class="edit-icon" @click.stop="handleToggle" id="toggle">
          <text
            class="iconfont fs-48 text-primary"
            :class="[isVoice ? 'icon-jianpan1' : 'icon-yuyin1']"
          />
        </view>
        <view
          class="btn-set"
          @click.stop="handleSetConst"
          :class="[
            (localVoice == 'voice-input' && !isVoice) ||
            (localVoice == 'voice-audio' && isVoice)
              ? 'icon-disabled'
              : '',
          ]"
        >
          设为常用
        </view>
        <button class="btn-save" @click.stop="handleComplete">完成</button>
      </view>
      <view
        class="send-main"
        :style="{ height: keyboardHeight + 'px' }"
        v-if="isVoice"
      >
        <template v-if="isVoice">
          <view v-show="isLongPress" class="send-gif">
            <image src="/static/images/voice-play.gif" mode=""></image>
          </view>
          <view class="send-main-btn">
            <!-- 语音输入提示 -->
            <view class="send-tips-box">
              <view v-if="!isLongPress" class="send-tips color-danger">
                按住说话
              </view>
              <template v-else>
                <view v-if="isMoveUp" class="send-tips color-danger">
                  松开取消
                </view>
                <view v-else class="send-tips"> 松开结束，上划取消 </view>
              </template>
            </view>
            <!-- 语音输入触发按钮 -->
            <button
              class="send-voice"
              :class="{ isLongPress: isLongPress, isMoveUp: isMoveUp }"
              @longpress="handleRecord($event, 'start')"
              @touchmove="handleRecord($event, 'move')"
              @touchend="handleRecord($event, 'end')"
            >
              <text
                class="send-voice-icon iconfont fs-48"
                :class="[isMoveUp ? 'icon-shanchu-anxia' : 'icon-yuyinxin']"
              >
              </text>
            </button>
          </view>
        </template>
      </view>
    </view>
    <view
      class="scs-voice-input-mask"
      @click.stop="onOutsideClick"
      :class="{
        hidden: !insideFocus,
      }"
    ></view>
    <!-- #endif -->
  </view>
</template>
<script>
import { showAuthTipModal, showManualAuth, voiceToText } from "@/common/utils";
export default {
  props: {
    value: {
      type: String,
      default: "",
    },
    // 输入框类型，注意该值或覆盖originAttrs中的type值
    type: { type: String, default: "textarea" },
    // 是否实时语音转文字
    realTimeVoice: { type: Boolean, default: false },
    /**
     * easyinput 组件原生属性
     * @type {Object}
     * 参考：https://uniapp.dcloud.net.cn/component/uniui/uni-easyinput.html#easyinput-props
     */
    originAttrs: {
      type: Object,
      default: () => {
        return {};
      },
    },
  },
  data() {
    return {
      insideFocus: false,
      focus: false, // 输入框聚焦受控值
      isVoice: true, //是否语音输入
      keyboardHeight: 260, // 键盘高度
      localVoice: "", // 本地存储状态
      recorderManager: uni.getRecorderManager(),
      isLongPress: false, // 语音是否长按
      isMoveUp: false, // 语音输入中时，是否上滑取消
      startY: 0, // 记录长按起始位置
      text: "",
      eventStartTime: 0,
    };
  },
  computed: {
    withDefaultAttrs() {
      const defaultAttrs = { placeholder: "请填写内容", autoHeight: true };
      if (this.type && this.type === "textarea") {
        defaultAttrs.maxlength = 1000;
      }
      if (this.originAttrs && typeof this.originAttrs === "object") {
        return { ...defaultAttrs, ...this.originAttrs };
      }
      return defaultAttrs;
    },
    // 输入框内容
    innerValue: {
      get() {
        return this.value;
      },
      set(val) {
        this.$emit("input", val);
      },
    },
  },
  created() {
    // #ifdef APP-PLUS
    const self = this;
    uni.onKeyboardHeightChange((res) => {
      if (res.height == 0) {
        setTimeout(() => {
          self.keyboardHeight = 260;
        }, 200);
      } else {
        self.keyboardHeight = 0;
      }
    });
    this.recorderManager.onFrameRecorded((e) => {
      console.log("录音回调", e);
    });
    // #endif
  },
  watch: {
    value: {
      handler(val) {
        this.innerValue = val;
      },
    },
    // #ifdef APP-PLUS
    isVoice: {
      handler() {
        this.$nextTick(() => {
          if (this.isVoice) {
            this.hideKeyboard();
          } else {
            this.$refs.inputObj.onFocus();
          }
        });
      },
    },
    // #endif
  },
  methods: {
    // 组件内部点击时，显示切换按钮
    onClickInside(e) {
      this.insideFocus = true;
    },
    // 组件外部点击时，离开组件，隐藏切换按钮
    onOutsideClick(e) {
      this.insideFocus = false;
    },
    // 输入框聚焦时，将语音输入关闭
    onFocus(e) {
      this.$nextTick(() => {
        this.isVoice = false;
      });
    },
    // 切换输入方式
    handleToggle() {
      if (this.isVoice) {
        this.$refs.inputObj.onFocus();
      } else {
        this.$refs.inputObj.onBlur();
        this.isVoice = true;
      }
    },
    hideKeyboard() {
      uni.hideKeyboard();
    },
    // 点击完成
    handleComplete() {
      this.insideFocus = false;
      // 点击自定义的完成按钮时，触发自定义的complete事件
      this.$emit("complete", this.innerValue);
    },
    // 设为常用
    handleSetConst() {
      if (
        (this.localVoice == "voice-input" && !this.isVoice) ||
        (this.localVoice == "voice-audio" && this.isVoice)
      ) {
        return;
      }
      this.localVoice = this.isVoice ? "voice-audio" : "voice-input";
      uni.setStorageSync("set-voice", this.localVoice);
    },
    // 语音按钮 按下、移动、松开
    async handleRecord(e, type) {
      const self = this;
      // #ifdef APP-PLUS
      switch (type) {
        case "start":
          let { bool, result } = await showAuthTipModal(
            "android.permission.RECORD_AUDIO"
          );
          if (!bool) return;
          if (!result && uni.getSystemInfoSync().platform == "android") {
            showManualAuth("android.permission.RECORD_AUDIO");
          } else {
            self.isLongPress = true;
            self.startY = e.touches[0].pageY;
            self.recorderManager.start({
              format: "mp3",
              // uni.getSystemInfoSync().platform === "ios" ? "pcm" : "amr",
              numberOfChannels: 1,
              sampleRate: 16000,
              frameSize: 2,
            });
            self.eventStartTime = new Date().getTime();
          }
          break;
        case "move":
          if (self.isLongPress && e.touches[0].pageY < self.startY - 100) {
            self.isMoveUp = true;
          } else {
            self.isMoveUp = false;
          }
          break;
        case "end":
          self.recorderManager.stop();
          self.recorderManager.onStop(async (res) => {
            if (!self.isMoveUp) {
              if (new Date().getTime() - self.eventStartTime < 1000) {
                uni.showToast({
                  icon: "none",
                  title: "录制时长小于一秒钟,请重新录音",
                });
              } else {
                const datas = voiceToText(res.tempFilePath);
                self.handleSendVoice(datas.content);
              }
            }
            self.isLongPress = false;
            self.isMoveUp = false;
          });
          break;
      }
      // #endif
      // #ifdef H5
      throw new Error("H5暂不支持语音输入");
      // #endif
    },
    // 返回并拼接语音内容
    handleSendVoice(words) {
      this.innerValue += words;
    },
  },
  beforeDestroy() {
    // #ifdef APP-PLUS
    uni.offKeyboardHeightChange(() => {});
    // #endif
  },
};
</script>
<style lang="less">
.scs-voice-input {
  flex: 1;
  overflow: auto;
  display: flex;
  flex-direction: column;
  background-color: #f5f5f5;

  .scs-voice-input-mask {
    position: fixed;
    width: 100vw;
    height: 100vh;
    // background-color: rgba(0, 0, 0, 0.3);
    left: 0;
    top: 0;
    z-index: 998;
    &.hidden {
      display: none;
    }
  }

  .scs-voice-input-content {
    flex: 1;
    padding: 0 12px;
    background: #fff;
    border-bottom: 1px solid #f5f5f5;
    display: flex;
    flex-direction: column;
    overflow: auto;
    border-radius: 8px;
    position: relative;
    z-index: 999;

    .scs-voice-input-desc {
      flex: 1 !important;
      width: 100%;
      word-wrap: break-word;

      &.placeholder,
      .uni-textarea-placeholder {
        color: #cccccc;
      }

      ::v-deep .uni-textarea-wrapper {
        min-height: 100%;
        width: 100%;
      }
    }

    .scs-voice-input-camera {
      width: 100%;
    }
  }

  .scs-voice-input-voice {
    height: auto;
    padding: 10px;
    position: fixed;
    bottom: 0;
    left: 0;
    width: 100vw;
    background-color: #fff;
    z-index: 999;
    box-sizing: border-box;
    box-shadow: 0 0 5px #ccc;

    .send-edit {
      height: 35px;
      line-height: 35px;
      display: flex;
      justify-content: space-between;
      align-items: center;

      .edit-icon {
        width: 60px;
      }

      .btn-save {
        height: 28px;
        line-height: 28px;
        background-color: #4697ec;
        font-size: 15px;
        color: #fff;
        border-radius: 6px;
        padding: 0 15px;
        margin: 0;
      }

      /* 按钮禁用样式 */
      button[disabled] {
        background-color: #cccccc !important;
        color: #fff !important;
        cursor: not-allowed;
      }

      .btn-set {
        height: 28px;
        line-height: 28px;
        font-size: 15px;
        background-color: #f6f6f6;
        font-size: 15px;
        border-radius: 6px;
        padding: 0 7px;
        color: #666666;

        &.icon-disabled {
          color: #ccc;
        }
      }
    }

    .send-main {
      width: 100%;
      display: flex;
      align-items: center;
      flex-direction: column;
      position: relative;

      .send-gif {
        width: 226px;
        height: 23px;
        border-radius: 8px;
        // background: #4697ec;
        display: flex;
        align-items: center;
        justify-content: center;
        margin-top: 20px;

        uni-image {
          width: 100%;
          height: 100%;
        }
      }

      .send-main-btn {
        width: 100%;
        height: 114px;
        position: absolute;
        top: calc(50% - 57px);

        .send-voice {
          width: 114px;
          height: 100%;
          margin: 0 auto;
          background: #4697ec;
          border-radius: 50%;
          text-align: center;
          line-height: 114px;

          &.isLongPress {
            background-color: #4697ec;
            color: #fff;
            border: none;
          }

          &.isMoveUp {
            background-color: #f0504f;
            color: #fff;
            border: none;
          }

          .send-voice-icon {
            color: #fff;
          }
        }
      }

      .change {
        font-size: 26px;
      }

      .send-tips-box {
        height: 30px;

        .send-tips {
          display: flex;
          align-items: center;
          justify-content: center;
          font-size: 16px;
          color: #666666;
        }
      }
    }
  }
}
</style>
